package com.ls.fw.data.search.impl.lucene.statement;

import java.util.Set;

import org.apache.lucene.index.Term;
import org.apache.lucene.queries.BooleanFilter;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.NumericRangeQuery;
import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TermRangeQuery;
import org.apache.lucene.search.WildcardQuery;

import com.google.common.collect.Sets;
import com.ls.fw.data.exception.DataException;
import com.ls.fw.data.exp.And;
import com.ls.fw.data.exp.Comparison;
import com.ls.fw.data.exp.Comparison.Operator;
import com.ls.fw.data.exp.Compound;
import com.ls.fw.data.exp.Expression;
import com.ls.fw.data.exp.Or;
import com.ls.fw.data.exp.Where;
import com.ls.fw.data.search.impl.lucene.bean.ValueType;
import com.ls.fw.data.search.impl.lucene.exception.IndexException;
import com.ls.fw.data.search.impl.lucene.query.builder.BooleanQueryBuilder;
import com.ls.fw.data.search.impl.lucene.util.IndexUtil;
import com.ls.fw.data.statement.Select;
import com.ls.fw.data.visitor.ExpressionVisitor;

public class WhereParser extends BaseParser{

	private static final Set<Operator> NOT_Operator_SET = Sets.newHashSet(
			Operator.N, Operator.ISN, Operator.NBETWEEN
			, Operator.NEQ,Operator.NLIKE,Operator.IS_NOT_EMPTY,Operator.IS_NULL);

	protected Where where = null;
	private boolean isQuery = true;
	
	 
	public WhereParser(Where where, boolean isQuery) {
		this.where = where;
		this.isQuery = isQuery;
	}

	public static void main(String[] args) {
		Select select =  new Select();
//		select.addFromItem("lisheng", "test1");
//		select.addField("*");
		select.addField("age_i").setAlias("age");
		select.addField("index_i").setAlias("index");
//		select.addFromItem("test");
//		select.addField("age_i");
//		select.addField("index_i");
//		select.addMethodField(AFunctions.COUNT);
		Compound e = Expression.and();
		Expression expression = Expression.or(Expression.comparison("index_i",
						Comparison.Operator.GT, 10), Expression
						.comparison("index_i", Comparison.Operator.LT,
								7));
		
		e.add(expression);
		e.add(Expression.comparison("name_s", "name%")
				.setOperator("like"));
		select.setWhere(e);
		WhereParser w = new WhereParser(select.getWhere(),true);
		System.out.println(w.parse());
	}
	
	public BooleanQuery parse(){
		BooleanQuery boolQuery = new BooleanQuery();
		if (where != null) {
			if (isQuery) {
				this.explanWhere(boolQuery, where);
			} else {
				throw new DataException("请调用parseFilter()方法!");
			}
		}else{
			boolQuery.add(new MatchAllDocsQuery(),Occur.MUST);
		}
		return boolQuery;
	}
	

	public BooleanFilter parseFilter(){
		BooleanFilter boolQuery = new BooleanFilter();
		if (where != null) {
			if (isQuery) {
				throw new DataException("请调用parse()方法!");
			} else {
				boolQuery = new BooleanFilter();
//				this.explanWhere(boolQuery,where);
			}
		} 
		return boolQuery;
	}
	
	
	private void explanWhere(BooleanQuery boolQuery,Where where) {
		 this.parser(where.getWhere(), true, boolQuery);
	}


	private void parser(Expression expression,boolean isAnd,BooleanQuery query){
		if(expression instanceof Or){
			Or or = (Or) expression;
			BooleanQuery subQuery = new BooleanQuery();
			query.add(subQuery,Occur.SHOULD);
			parser(or,subQuery);
		}else if(expression instanceof And){
			BooleanQuery subQuery = new BooleanQuery();
			query.add(subQuery,Occur.MUST);
			And and = (And) expression;
			parser(and,subQuery);
		}else {
//			list.add((isAnd?"AND":"OR")+" "+expression);
			if(expression instanceof Comparison){
				if(isAnd){
					query.add(make((Comparison) expression),Occur.MUST);
				}else{
					query.add(make((Comparison) expression),Occur.SHOULD);
				}
			}else{ 
			}
			
			
		}
	}


	private void parser(And compound,final BooleanQuery query){
		compound.accept(new ExpressionVisitor() {
			
			@Override
			public void visit(Expression expression) {
				parser(expression,true,query);
			}
		});
	}
	
	private void parser(Or compound,final BooleanQuery query){
		compound.accept(new ExpressionVisitor() {
			
			@Override
			public void visit(Expression expression) {
				parser(expression,false,query);
			}
		});
	}
	
	/**
	 * 构建过滤条件
	 * 
	 * @param boolFilter
	 * @param expr
	 * @param expr
	 * @return
	 */
	protected Query make(Comparison cond)  {

		String name = cond.getName();
		Object value = cond.getValue();
		Object value1 = cond.getValue1();

		Query x = null;
		x = this.make(cond, name, value, value1,cond.getBoost());
		return x;
	}

	private Query make(Comparison cond, String name, Object value, Object value1,float boost)  {
		Query condition = null;
		Operator oper = cond.getOperator();
		switch (oper) {
		case BETWEEN:
		case NBETWEEN:
				condition = this.getBetweenQuery(name, value
						, value1,Operator.BETWEEN);
			break;
		case IS:
		case EQ:
		case EQ1:
		case N:
		case ISN:
		case NEQ:
			condition = this.getEqualQuery(name, value);
			break;
		case GT:
			condition = this.getBetweenQuery(name, value
					, value1,Operator.GT);
			break;
		case GTE:
			condition = this.getBetweenQuery(name, value
					, value1,Operator.GTE);
			break;
		case IN:{
			 Object v = value;
			 if(v!=null){
				 String _value = String.valueOf(v);
				 String[] vals = _value.split(",");
				 BooleanQueryBuilder in = new  BooleanQueryBuilder();
				 ValueType t = null;
				 t = IndexUtil.getValueType(value.getClass());
				 for (String string : vals) {
					 condition = this.getEqualQuery(name, parser(string,t));
					 in.should(condition);
					 condition = null;
				}
				condition = in.get();
			 }}
			break;
		case NIN:{
			Object v = value;
			 if(v!=null){
				 String _value = String.valueOf(v);
				 String[] vals = _value.split(",");
				 BooleanQueryBuilder in = new  BooleanQueryBuilder();
				 ValueType t = null;
					t = IndexUtil.getValueType(value.getClass());
				 for (String string : vals) {
					 condition = this.getEqualQuery(name, parser(string,t));
					 in.must(BooleanQueryBuilder.not(condition));
					 condition = null;
				}
				condition = in.get();
			 }}
			break;	
		case IS_EMPTY:
		case IS_NOT_EMPTY:
			condition = this.getEqualQuery(name, "''" );
			break;
		case IS_NOT_NULL:
		case IS_NULL:
			condition = this.getBetweenQuery(name, "''"
					, WildcardQuery.WILDCARD_STRING,Operator.IS_NULL);
			break;
		case PRE_LIKE:
			condition = new WildcardQuery(new Term(name, WildcardQuery.WILDCARD_STRING+String.valueOf(value)));
			break;
		case LAST_LIKE:
//			condition = new WildcardQuery(new Term(name, String.valueOf(value)+WildcardQuery.WILDCARD_STRING));
			condition = new PrefixQuery(new Term(name, String.valueOf(value)));
			break;
		case NOT_CHAR:
		case CHAR:
			 condition = new WildcardQuery(new Term(name, WildcardQuery.WILDCARD_CHAR+String.valueOf(value)+ WildcardQuery.WILDCARD_CHAR));
			 break;
		case PRE_CHAR:
			condition = new WildcardQuery(new Term(name, WildcardQuery.WILDCARD_CHAR+String.valueOf(value)));
			break;
		case LAST_CHAR:
			condition = new WildcardQuery(new Term(name, String.valueOf(value)+WildcardQuery.WILDCARD_CHAR));
			break;
		case LIKE:
		case NLIKE:{
				String v = value.toString();
				if(v.startsWith("%")){
					v = WildcardQuery.WILDCARD_STRING + v.substring(v.indexOf("%")+1);
				}
				if(v.endsWith("%")){
					v =  v.substring(0,v.lastIndexOf("%")) + WildcardQuery.WILDCARD_STRING;
				}
				 condition = new WildcardQuery(new Term(name, v));
			} 
			 break;
		case LT:
			condition = this.getBetweenQuery(name, value1
					, value,Operator.LT);
			break;
		case LTE:
			condition = this.getBetweenQuery(name, value1
					, value,Operator.LTE);
			break;
		
		
		case UNKOWN:
		default:
			throw new DataException("not define type " + cond.getName());
		}
		if(condition!=null){
			if(NOT_Operator_SET.contains(oper)){
				condition = BooleanQueryBuilder.not(condition);
			}
			condition.setBoost(boost);
		}
		return condition;
	}

	private Object parser(String val,ValueType t){
		if(t==null){
			return val;
		}
		Object value1 = val;
		 switch(t){
			case DOUBLE:
				value1 = Double.parseDouble(val);
				break;
			case FLOAT:
				value1 = Float.parseFloat(val);
				break;
			case LONG:
				value1 = Long.parseLong(val);
				break;
			case INT:
				value1 = Integer.parseInt(val);
				break;
			default:
				break;
			}
		 return value1;
	}

	private Query getBetweenQuery(String name,Object m_value
			,Object m_value1,Operator  operatorType){
		Object value = m_value;
		Object value1 = m_value1;
		ValueType t = IndexUtil.getValueType(value);
		ValueType t1 = IndexUtil.getValueType(value1);
		t = ValueType.getValueType(Math.max(t.getBit(),t1.getBit()));
		boolean min_e = true,max_e = true;
		switch(operatorType){
			case BETWEEN:
				break;
			case NBETWEEN:
				break;
			case IS_NULL:
				break;
			case IS_NOT_NULL:
				break;
			case GT:
				{
					min_e = false;
					switch(t){
					case DOUBLE:
						value1 = Double.MAX_VALUE;
						break;
					case FLOAT:
						value1 = Float.MAX_VALUE;
						break;
					case LONG:
						value1 = Long.MAX_VALUE;
						break;
					case INT:
						value1 = Integer.MAX_VALUE;
						break;
					case STRING:
						value1 = WildcardQuery.WILDCARD_STRING;
						break;
					default:
						value1 = Long.MAX_VALUE;
						break;
					}
				}
				break;
			case GTE:
				{
					switch(t){
					case DOUBLE:
						value1 = Double.MAX_VALUE;
						break;
					case FLOAT:
						value1 = Float.MAX_VALUE;
						break;
					case LONG:
						value1 = Long.MAX_VALUE;
						break;
					case INT:
						value1 = Integer.MAX_VALUE;
						break;
					case STRING:
						value1 = WildcardQuery.WILDCARD_STRING;
						break;
					default:
						value1 = Long.MAX_VALUE;
						break;
					}
				}
				break;
			case LT:
				{
					max_e = false;
					switch(t){
					case DOUBLE:
						value = Double.MIN_VALUE;
						break;
					case FLOAT:
						value = Float.MIN_VALUE;
						break;
					case LONG:
						value = Long.MIN_VALUE;
						break;
					case INT:
						value = Integer.MIN_VALUE;
						break;
					case STRING:
						value = WildcardQuery.WILDCARD_STRING;
						break;
					default:
						value = 0;
						break;
					}
				}
				break;
			case LTE:
				{
					switch(t){
					case DOUBLE:
						value1 = Double.MIN_VALUE;
						break;
					case FLOAT:
						value1 = Float.MIN_VALUE;
						break;
					case LONG:
						value1 = Long.MIN_VALUE;
						break;
					case INT:
						value1 = Integer.MIN_VALUE;
						break;
					case STRING:
						value1 = WildcardQuery.WILDCARD_STRING;
						break;
					default:
						value1 = 0;
						break;
					}
				}
				break;
			default:
				break;
		}
		Query query = null;
		switch(t){
			case NONE:
				throw new IndexException("value不能为空！");
			case DOUBLE:
				query = NumericRangeQuery.newDoubleRange(name, Double.valueOf(String.valueOf(value)), Double.valueOf(String.valueOf(value1)), min_e, max_e);
				break;
			case FLOAT:
				query = NumericRangeQuery.newFloatRange(name, Float.valueOf(String.valueOf(value)), Float.valueOf(String.valueOf(value1)), min_e, max_e);
				break;
			case LONG:
				query = NumericRangeQuery.newLongRange(name, Long.valueOf(String.valueOf(value)), Long.valueOf(String.valueOf(value1)), min_e, max_e);
				break;
			case INT:
				query = NumericRangeQuery.newIntRange(name, Integer.valueOf(String.valueOf(value)), Integer.valueOf(String.valueOf(value1)), min_e, max_e);
				break;
			case STRING:
				query = TermRangeQuery.newStringRange(name, String.valueOf(value), String.valueOf(value1),min_e, max_e);
				break;
			default:
				throw new IndexException(name+"的类型为"+t+"，不支持范围查询！");
		}
		return query;
	}
	
	private Query getEqualQuery(String name,Object value){
		ValueType t = IndexUtil.getValueType(value);
		Query query = null;
		switch(t){
			case NONE:
				throw new IndexException("value不能为空！");
			case DOUBLE:
				query = NumericRangeQuery.newDoubleRange(name, Double.valueOf(String.valueOf(value)), Double.valueOf(String.valueOf(value)), true, true);
				break;
			case FLOAT:
				query = NumericRangeQuery.newFloatRange(name, Float.valueOf(String.valueOf(value)), Float.valueOf(String.valueOf(value)), true, true);
				break;
			case LONG:
				query = NumericRangeQuery.newLongRange(name, Long.valueOf(String.valueOf(value)), Long.valueOf(String.valueOf(value)), true, true);
				break;
			case INT:
				query = NumericRangeQuery.newIntRange(name, Integer.valueOf(String.valueOf(value)), Integer.valueOf(String.valueOf(value)), true, true);
				break;
			case STRING:
				query = new TermQuery(new Term(name, String.valueOf(value)));
				break;
		}
		return query;
	}

}
